﻿using System.Web.Security;
using OperationPlugins;
using OperationPlugins.Compositions;
using OperationPlugins.Mvc.Filters;
using OperationPlugins.Validators;
using PerpetuumSoft.Knockout;
using System;
using System.Collections.Generic;
using System.Data.Entity.Validation;
using System.Linq;
using System.Transactions;
using System.Web.Mvc;
using TimeProject.Engine;
using TimeProject.Engine.DbContent;
using TimeProject.Engine.DbModels;
using TimeProject.Engine.Providers;
using TimeProject.WebSite.Controllers.Actions;
using TimeProject.WebSite.Filters;
using TimeProject.WebSite.ViewModels;
using WebMatrix.WebData;
using System.ComponentModel.Composition;

namespace TimeProject.WebSite.Controllers
{
    [Authorize]
    [UserInfoToViewData]
    [NotificationRouterForAllActions]
    public sealed partial class TimeProjectController : KnockoutController
    {
        /// <summary>
        /// IUserOptionProvider
        /// </summary>
        private static IEnumerable<IUserOptionProvider> UserOptionProviders { get; set; }

        /// <summary>
        /// IEntityPublicIdProvider
        /// </summary>
        private static IEntityPublicIdProvider EntityPublicIdProvider { get; set; }

        /// <summary>
        /// Initializes a new instance of the TimeProjectController class.
        /// </summary>
        public TimeProjectController()
        {
            RequestContext.Current.Initialize();   
        }

        /// <summary>
        /// Gets the db context.
        /// </summary>
        private TimeProjectDbContext Db
        {
            get { return RequestContext.Current.Db; }
        }

        /// <summary>
        /// Gets the user id of the current request user.
        /// </summary>
        private int UserId
        {
            get { return RequestContext.Current.CurrentUser.UserId; }
        }

        /// <summary>
        /// Creates a JsonResult object that serializes the specified object to JavaScript Object Notation (JSON). Before serialization 
        /// the specified action method will be invoked, but only if the ViewModelBase object is valid.
        /// </summary>
        /// <param name="vm">The JavaScript object graph to serialize.</param>
        /// <param name="actionMethod">The action method.</param>
        /// <returns>The JSON result object that serializes the specified object to JSON format. The result object that is prepared by this method is written to the response by the ASP.NET MVC framework when the object is executed.</returns>
        private JsonResult Json(ViewModelBase vm, Action actionMethod)
        {
            if (vm == null)
            {
                throw new ArgumentNullException("vm");
            }

            if (actionMethod == null)
            {
                throw new ArgumentNullException("actionMethod");
            }

            if (IsValid(vm))
            {
                try
                {
                    using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions {IsolationLevel = IsolationLevel.Serializable}))
                    {
                        actionMethod.Invoke();

                        transaction.Complete();
                    }
                }
                catch (DbEntityValidationException ex)
                {
                    vm.Messages.Reset();

                    foreach (DbEntityValidationResult result in ex.EntityValidationErrors)
                    {
                        foreach (DbValidationError error in result.ValidationErrors)
                        {
                            vm.Messages.AddErrorMessage(error.ErrorMessage);
                        }
                    }
                }
            }

            return Json(vm);
        }

        /// <summary>
        /// Creates a JsonResult object that serializes the specified object to JavaScript Object Notation (JSON).
        /// </summary>
        /// <param name="vm">The JavaScript object graph to serialize.</param>
        /// <returns>The JSON result object that serializes the specified object to JSON format. The result object that is prepared by this method is written to the response by the ASP.NET MVC framework when the object is executed.</returns>
        private JsonResult Json(ViewModelBase vm)
        {
            var values = new List<string>();

            if (TempData.ContainsKey("info"))
            {
                var existing = TempData["info"] as IEnumerable<string>;
                if (existing != null)
                {
                    values.AddRange(existing);
                }
                TempData.Remove("info");
            }

            var distinct = new List<string>();
            distinct.AddRange(values);
            distinct.AddRange(ValidationResultMessage.ToStrings(OperationPluginsContext.Current.Notifications));

            if (vm.Messages.ContainsNotifications)
            {
                distinct.Add(vm.Messages.Text);
            }

            var messages = distinct.Distinct().ToArray();

            values.Clear();
            values.AddRange(messages);

            TempData.Add("info", values.AsEnumerable());

            return base.Json(vm);
        }

        private bool IsValid(ViewModelBase vm)
        {
            if (vm == null)
            {
                throw new ArgumentNullException("vm");
            }

            bool valid = ModelState.IsValid;

            vm.Messages.Reset();

            if (valid == false)
            {
                foreach (ModelState state in ModelState.Values)
                {
                    foreach (ModelError error in state.Errors)
                    {
                        if (String.IsNullOrEmpty(error.ErrorMessage) == false)
                        {
                            vm.Messages.AddErrorMessage(error.ErrorMessage);
                        }
                    }
                }
            }

            return valid;
        }

        private bool SaveDbContext(ViewModelBase vm = null)
        {
            bool succeed = true;

            try
            {
                Db.SaveChanges();
            }
            catch (DbEntityValidationException ex)
            {
                succeed = false;

                foreach (DbEntityValidationResult result in ex.EntityValidationErrors)
                {
                    foreach (DbValidationError error in result.ValidationErrors)
                    {
                        if (vm != null)
                        {
                            vm.Messages.AddErrorMessage(error.ErrorMessage);    
                        }

                        ModelState.AddModelError("", error.ErrorMessage);
                    }
                }
            }

            return succeed;
        }

        private ActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "TimeProject");
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                RequestContext.Current.Dispose();
            }

            base.Dispose(disposing);
        }


        /// <summary>
        /// Initialize.
        /// </summary>
        [Export(typeof(IInitializable))]
        private sealed class Initializer : IInitializable
        {
            void IInitializable.Initialize(InitializationContext context)
            {
                UserOptionProviders = context.CompositionContainer.GetPlugins<IUserOptionProvider>();

                EntityPublicIdProvider = context.CompositionContainer.GetPlugin<IEntityPublicIdProvider>();
            }
        }



        #region OLD

        private TActionHandler CreateHandler<TActionHandler>() where TActionHandler : ActionHandlerBase, new()
        {
            TActionHandler handler = new TActionHandler();

            return handler;
        }

        [HttpPost]
        [DisablePluginsValidation]
        public ActionResult AddLine(UsersViewModel vm)
        {
            for (int i = 0; i < 1000; i++)
                vm.Users.Add(new UserViewModel() { UserId = vm.Users.Count + 1, Username = DateTime.Now.ToLongTimeString(), FullName = "masaX" });


            //            return RedirectToAction("Users");

            //            model.AddLine();
            return Json(vm);
        }

        //
        // GET: /App/Users
        public ActionResult Users()
        {
            //MyCustomQuery[] items = Db.Query(new MyCustomQuery { UserId = 1 });

            UsersActionHandler handler = CreateHandler<UsersActionHandler>();
            UsersViewModel vm = handler.GetUsersModel();
            vm.Users.Clear();

            for (int i = 0; i < 10; i++)
                vm.Users.Add(new UserViewModel() { UserId = vm.Users.Count + 1, Username = DateTime.Now.ToLongTimeString(), FullName = "masaX" });

            return View(vm);


            //InitializeViewBag("Cart editor");

            //    var model = new UsersModel();
            //    model.Users = new List<UserModel>();

            //    model.JokuArvo = DateTime.Now.ToShortTimeString();

            //    model.Users.Add(new UserModel() { UserId = 1, FullName = "Matti Meikäläinen", UserName = "mattim", ProjectId = 0, TaskId = 0 });
            //    model.Users.Add(new UserModel() { UserId = 2, FullName = "Teppo Teräväinen", UserName = "tteppo", ProjectId = 0, TaskId = 1 });
            //    model.Users.Add(new UserModel() { UserId = 3, FullName = "Matti Nykänen", UserName = "masa", ProjectId = 1, TaskId = 0 });




            //    model.DataBase = new List<ProjectModel>();
            //    model.DataBase.Add(new ProjectModel
            //    {
            //        Name = "Category 1",
            //        Tasks = new List<TaskModel>
            //{
            //  new TaskModel {Name = "Product 1-1"},
            //  new TaskModel {Name = "Product 1-2"},
            //}
            //    });
            //    model.DataBase.Add(new ProjectModel
            //    {
            //        Name = "Category 2",
            //        Tasks = new List<TaskModel>
            //{
            //  new TaskModel {Name = "Product 2-1"},
            //  new TaskModel {Name = "Product 2-2"},
            //}
            //    });

            //    model.ProjectId = new List<int> { 0, 1 };
            //    model.TaskId = new List<List<int>>
            //           {
            //             new List<int> {0, 1},
            //             new List<int> {0, 1}
            //           };

            //    return View(model);
        }

        [HttpPost]
        public ActionResult ActivateUser(UsersViewModel viewModel, int index)
        {
            if (IsValid(viewModel))
            {
                UserViewModel userView = viewModel.GetUserByIndex(index);
                userView.IsActive = true;
                return Json(viewModel);
            }
            else
            {
                return Json(viewModel);                
            }
        }

        [HttpPost]
        public ActionResult PassivateUser(UsersViewModel viewModel, int index)
        {
            UserViewModel userView = viewModel.GetUserByIndex(index);
            userView.IsActive = false;
            return Json(viewModel);
        }
     
        [HttpPost]
        public ActionResult Save(UsersViewModel viewModel)
        {
            return Json(viewModel, () =>
            {
                //UserViewModel u = viewModel.Users.FirstOrDefault(x => x.UserId == 0);

                //if (u != null)
                //{

                //    User user = new User();
                //    user.FullName = u.FullName;


                //    WebSecurity.CreateUserAndAccount(u.Username, "sisu",
                //                                    propertyValues: new
                //                                    {
                //                                        FullName = "dummy",
                //                                        IsActive = true
                //                                    });


                //    int userId = WebSecurity.GetUserId(u.Username);

                //    user = base.Db.GetUser(userId);
                //    base.Db.Entry(user).State = EntityState.Modified;


                //    user.FullName = "LIIAN PITKÄ NIMI LIIAN PITKÄ NIMI LIIAN PITKÄ NIMI LIIAN PITKÄ NIMI LIIAN PITKÄ NIMI LIIAN PITKÄ NIMI";

                //    // Tämä heittää exceptionin...
                //    base.Db.SaveChanges();

                //}


                viewModel.Messages.Reset();
                viewModel.Messages.AddErrorMessage("Nyt kello on paljon 1");
                viewModel.Messages.AddErrorMessage("Nyt kello on paljon 2");
                viewModel.Messages.AddErrorMessage("Nyt kello on paljon 3");
                viewModel.Messages.AddErrorMessage("Nyt kello on paljon 4");
                viewModel.Messages.AddErrorMessage("Nyt kello on paljon 5");
                //          viewModel.Messages.AddNofificationMessage("Nyt kello on paljon");
                bool b = true;
            });
        }

        //
        // GET: /App/Index
        public ActionResult Index()
        {
            ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
          
            //    var x = Roles.GetRolesForUser(WebSecurity.CurrentUserName);

            return RedirectToAction("Users");
        }

        //
        // POST: /App/Disassociate

        //[HttpPost]
        //[ValidateAntiForgeryToken]
        //public ActionResult Disassociate(string provider, string providerUserId)
        //{
        //    string ownerAccount = OAuthWebSecurity.GetUserName(provider, providerUserId);
        //    ManageMessageId? message = null;

        //    // Only disassociate the account if the currently logged in user is the owner
        //    if (ownerAccount == User.Identity.Name)
        //    {
        //        // Use a transaction to prevent the user from deleting their last login credential
        //        using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.Serializable }))
        //        {
        //            bool hasLocalAccount = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
        //            if (hasLocalAccount || OAuthWebSecurity.GetAccountsFromUserName(User.Identity.Name).Count > 1)
        //            {
        //                OAuthWebSecurity.DeleteAccount(provider, providerUserId);
        //                scope.Complete();
        //                message = ManageMessageId.RemoveLoginSuccess;
        //            }
        //        }
        //    }

        //    return RedirectToAction("Manage", new { Message = message });
        //}

      
        //
        // POST: /App/ExternalLogin

        //[HttpPost]
        //[AllowAnonymous]
        //[ValidateAntiForgeryToken]
        //public ActionResult ExternalLogin(string provider, string returnUrl)
        //{
        //    return new ExternalLoginResult(provider, Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
        //}

        //
        // GET: /App/ExternalLoginCallback

        //[AllowAnonymous]
        //public ActionResult ExternalLoginCallback(string returnUrl)
        //{
        //    AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
        //    if (!result.IsSuccessful)
        //    {
        //        return RedirectToAction("ExternalLoginFailure");
        //    }

        //    if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
        //    {
        //        return RedirectToLocal(returnUrl);
        //    }

        //    if (User.Identity.IsAuthenticated)
        //    {
        //        // If the current user is logged in add the new account
        //        OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
        //        return RedirectToLocal(returnUrl);
        //    }
        //    else
        //    {
        //        // User is new, ask for their desired membership name
        //        string loginData = OAuthWebSecurity.SerializeProviderUserId(result.Provider, result.ProviderUserId);
        //        ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(result.Provider).DisplayName;
        //        ViewBag.ReturnUrl = returnUrl;
        //        return View("ExternalLoginConfirmation", new RegisterExternalLoginViewModel { UserName = result.UserName, ExternalLoginData = loginData });
        //    }
        //}

        //
        // POST: /App/ExternalLoginConfirmation

        //[HttpPost]
        //[AllowAnonymous]
        //[ValidateAntiForgeryToken]
        //public ActionResult ExternalLoginConfirmation(RegisterExternalLoginViewModel viewModel, string returnUrl)
        //{
        //    string provider = null;
        //    string providerUserId = null;

        //    if (User.Identity.IsAuthenticated || !OAuthWebSecurity.TryDeserializeProviderUserId(viewModel.ExternalLoginData, out provider, out providerUserId))
        //    {
        //        return RedirectToAction("Manage");
        //    }

        //    if (ModelState.IsValid)
        //    {
        //        // Insert a new user into the database
        //        User user = Db.Users.FirstOrDefault(u => u.Username.ToLower() == viewModel.UserName.ToLower());
        //        // Check if user already exists
        //        if (user == null)
        //        {
        //            // Insert name into the profile table
        //            Db.Users.Add(new User { Username = viewModel.UserName });
        //            Db.SaveChanges();

        //            OAuthWebSecurity.CreateOrUpdateAccount(provider, providerUserId, viewModel.UserName);
        //            OAuthWebSecurity.Login(provider, providerUserId, createPersistentCookie: false);

        //            return RedirectToLocal(returnUrl);
        //        }
        //        else
        //        {
        //            ModelState.AddModelError("UserName", "User name already exists. Please enter a different user name.");
        //        }
        //    }

        //    ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(provider).DisplayName;
        //    ViewBag.ReturnUrl = returnUrl;
        //    return View(viewModel);
        //}

        ////
        //// GET: /App/ExternalLoginFailure

        //[AllowAnonymous]
        //public ActionResult ExternalLoginFailure()
        //{
        //    return View();
        //}

        //[AllowAnonymous]
        //[ChildActionOnly]
        //public ActionResult ExternalLoginsList(string returnUrl)
        //{
        //    ViewBag.ReturnUrl = returnUrl;
        //    return PartialView("_ExternalLoginsListPartial", OAuthWebSecurity.RegisteredClientData);
        //}

        //[ChildActionOnly]
        //public ActionResult RemoveExternalLogins()
        //{
        //    ICollection<OAuthAccount> accounts = OAuthWebSecurity.GetAccountsFromUserName(User.Identity.Name);
        //    List<ExternalViewLogin> externalLogins = new List<ExternalViewLogin>();
        //    foreach (OAuthAccount account in accounts)
        //    {
        //        AuthenticationClientData clientData = OAuthWebSecurity.GetOAuthClientData(account.Provider);

        //        externalLogins.Add(new ExternalViewLogin
        //        {
        //            Provider = account.Provider,
        //            ProviderDisplayName = clientData.DisplayName,
        //            ProviderUserId = account.ProviderUserId,
        //        });
        //    }

        //    ViewBag.ShowRemoveButton = externalLogins.Count > 1 || OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
        //    return PartialView("_RemoveExternalLoginsPartial", externalLogins);
        //}

        //public enum ManageMessageId
        //{
        //    ChangePasswordSuccess,
        //    SetPasswordSuccess,
        //    RemoveLoginSuccess,
        //}

        //internal class ExternalLoginResult : ActionResult
        //{
        //    public ExternalLoginResult(string provider, string returnUrl)
        //    {
        //        Provider = provider;
        //        ReturnUrl = returnUrl;
        //    }

        //    public string Provider { get; private set; }
        //    public string ReturnUrl { get; private set; }

        //    public override void ExecuteResult(ControllerContext context)
        //    {
        //        OAuthWebSecurity.RequestAuthentication(Provider, ReturnUrl);
        //    }
        //}
        #endregion
    }
}
